﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Doo.DataClass
{
    /*
     * 目前实现了对单个表的监测，暂未实现对多个表join操作的监测
     * 
     * 列依赖的实现：对每个表保存当前需要的列名及引用计数，在发生更改时（新增和删除可不用列依赖，因为必然更改），将更改的列查出来，依次比较即可。
     * 
     * 页依赖：该依赖需要提供一个查询以确定所修改的数据所位于的位置来刷新或失效缓存。
     * 
     */

    #region 条件值依赖
    public abstract class ValueBase
    {
        /// <summary>
        /// 命中
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public abstract bool IsChanged(object value);
    }
    class SingleValue : ValueBase
    {
        public object Value;
        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (obj is SingleValue)
            {
                return ((SingleValue)obj).Value.Equals(Value);
            }
            return false;
        }
        public override bool IsChanged(object value)
        {
            return value.Equals(Value);
        }
    }
    /// <summary>
    /// 范围值
    /// </summary>
    class RangeValue : ValueBase
    {
        public IComparable MinValue;
        public IComparable MaxValue;
        public bool EqualMin;
        public bool EqualMax;
        public override int GetHashCode()
        {
            return MinValue.GetHashCode() ^ MaxValue.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (!(obj is RangeValue)) return false;
            RangeValue tgt = (RangeValue)obj;
            return tgt.MinValue.Equals(MinValue) && tgt.MaxValue.Equals(MaxValue);
        }
        public override bool IsChanged(object value)
        {
            IComparable cv = value as IComparable;
            return cv != null
                &&
                    (MinValue != null && cv.CompareTo(MinValue) > (EqualMin ? -1 : 0)) //小于最小值
                &&
                    (MaxValue != null && cv.CompareTo(MaxValue) < (EqualMax ? 1 : 0));//大于最大值
        }
    }
    /// <summary>
    /// 多值
    /// </summary>
    class MultiValue : ValueBase
    {
        public object[] Values;
        public override int GetHashCode()
        {
            return Values.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (!(obj is MultiValue)) return false;
            MultiValue tgt = (MultiValue)obj;
            return tgt.Values.Equals(Values);
        }
        public override bool IsChanged(object value)
        {
            return Values.Contains(value);
        }
    }
    #endregion

    #region 条件值依赖Attribute

    /// <summary>
    /// 用在ListClass的属性上时，如果EnableCache属性为真，将自动产生缓存和列值依赖。
    /// 缓存键由类型、选择器、参数和值(Base64编码)、分页构成。
    /// 例如TEntity%Selector%DateSt_23423423%DateEn_2345235978%0%999
    /// </summary>
    public abstract class ValueBaseAttribute : Attribute
    {
        /// <summary>
        /// 对应的列名
        /// </summary>
        public string Field { get; set; }

        /// <summary>
        /// [此属性暂时无效]是否将NULL值也纳入比较（不推荐）
        /// </summary>
        public bool IncludeNULL { get; set; }

        public abstract ValueBase GetValue(object value);
    }

    /// <summary>
    /// 表示修饰的属性具有唯一值
    /// </summary>
    public class SingleValueAttribute : ValueBaseAttribute
    {
        public override ValueBase GetValue(object value)
        {
            return new SingleValue { Value = value };
        }

    }

    /// <summary>
    /// 一个边界值，当通知更改的值落在此区间时，就视为缓存失效
    /// </summary>
    public class RangeValueAttribute : ValueBaseAttribute
    {
        /// <summary>
        /// 是否与边界值相等
        /// </summary>
        public bool IsEqual { get; set; }

        /// <summary>
        /// 属性值是否是区间最大值，否为最小值
        /// </summary>
        public bool IsMax { get; set; }

        public override ValueBase GetValue(object value)
        {
            var vb = new RangeValue();
            if (IsMax)
            {
                vb.MaxValue = value as IComparable;
                vb.EqualMax = IsEqual;
            }
            else
            {
                vb.MinValue = value as IComparable;
                vb.EqualMin = IsEqual;
            }
            return vb;
        }
    }

    /// <summary>
    /// 此Attribute修饰的必须是一个数组，当通知更改的值为此数组中任一值时，就视为缓存失效
    /// </summary>
    public class MultiValueAttribute : ValueBaseAttribute
    {
        public override ValueBase GetValue(object value)
        {
            return new MultiValue { Values = value as object[] };
        }
    }

    #endregion

    #region 列依赖Attribute

    /// <summary>
    /// 列依赖，当缓存通知发生时，检查此列是否已更改，如果已经更改，则删除它
    /// 如果不加此Attribute，则：如果属性名对应默认成为依赖列，否则忽略
    /// </summary>
    public class EntityFieldAttribute : Attribute
    {
        /// <summary>
        /// 是否忽略此列，用于某些从外部join进来的列
        /// </summary>
        public bool Ignore { get; set; }

        /// <summary>
        /// 该列的实际名称
        /// </summary>
        public string Field { get; set; }
    }

    #endregion

    #region 实体缓存帮助类

    /// <summary>
    /// 实体缓存帮助类，用此类注册需要缓存的类型和字段和值，并在该值相关的记录被更新时通知缓存
    /// 支持具体值、区间值、松散值
    /// </summary>
    public class EntityCacheHelper
    {
        //词典：类型->字段->值->缓存键
        static EntityCacheHelper _instance;

        /// <summary>
        /// 实体缓存帮助类的单例
        /// </summary>
        public static EntityCacheHelper Instance
        {
            get
            {
                if (_instance == null) _instance = new EntityCacheHelper();
                return _instance;
            }
        }

        static Dictionary<Type, Dictionary<System.Reflection.PropertyInfo, ValueBaseAttribute[]>> typeToAttrs = new Dictionary<Type, Dictionary<System.Reflection.PropertyInfo, ValueBaseAttribute[]>>();

        /// <summary>
        /// 某个类型（表或选择器）所对应的列名
        /// </summary>
        static Dictionary<Type, HashSet<string>> typeToFields = new Dictionary<Type, HashSet<string>>();

        /// <summary>
        /// 表与选择器
        /// </summary>
        static Dictionary<Type, HashSet<Type>> typeToSelectors = new Dictionary<Type, HashSet<Type>>();

        /// <summary>
        /// 注册缓存依赖列，如果某类型没有注册，则任何列的更改都导致缓存失效
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="Td"></typeparam>
        public void RegisterTypeFieldsIfNot<TEntity, Td>()
        {
            Type td = typeof(Td);
            if (typeToFields.ContainsKey(td)) return;//已经注册了

            lock (typeToFields)
            {
                if (typeToFields.ContainsKey(td)) return;//已经注册了

                Type te = typeof(TEntity);

                #region 注册选择器

                HashSet<Type> selectors;
                if (!typeToSelectors.TryGetValue(te, out selectors))
                {
                    selectors = new HashSet<Type>();
                    typeToSelectors.Add(te, selectors);
                }
                selectors.Add(td);

                #endregion

                List<string> fieldList = new List<string>();

                #region 查找字段
                var props = td.GetProperties(System.Reflection.BindingFlags.Public);
                foreach (var p in props)
                {
                    string field = p.Name;
                    //1.检查EntityFieldAttribute
                    var attrs = p.GetCustomAttributes(typeof(EntityFieldAttribute), true);
                    if (attrs.Any())
                    {
                        var a = attrs[0] as EntityFieldAttribute;
                        if (a.Ignore) continue;//忽略此属性

                        if (!string.IsNullOrEmpty(a.Field)) field = a.Field;
                    }

                    if (te.GetProperty(field) == null) continue;//不存在的列名直接跳过

                    fieldList.Add(field);
                }
                #endregion

                typeToFields.Add(td, new HashSet<string>(fieldList));
            }
        }

        /// <summary>
        /// 获取实体已注册的列
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        internal HashSet<string> GetRegisteredEntityFields<TEntity>()
        {
            var selectors = typeToSelectors[typeof(TEntity)];
            HashSet<string> result = new HashSet<string>();
            foreach (var s in selectors)
            {
                result.UnionWith(typeToFields[s]);
            }
            return result;
        }

        /// <summary>
        /// 获取类型中注册的缓存依赖字段Attributes
        /// </summary>
        /// <param name="T"></param>
        /// <returns></returns>
        internal static Dictionary<System.Reflection.PropertyInfo, ValueBaseAttribute[]> GetTypeValueBaseProperties(Type T)
        {
            if (!typeToAttrs.ContainsKey(T))
            {
                lock (typeToAttrs)
                {
                    if (!typeToAttrs.ContainsKey(T))
                    {
                        var props = T.GetProperties();
                        var pdict = new Dictionary<System.Reflection.PropertyInfo, ValueBaseAttribute[]>();
                        foreach (var p in props)
                        {
                            var attrs = p.GetCustomAttributes(typeof(ValueBaseAttribute), true);
                            if (attrs == null || attrs.Length == 0) continue;
                            pdict.Add(p, attrs.Cast<ValueBaseAttribute>().ToArray());
                        }
                        if (pdict.Count == 0)
                        {
                            typeToAttrs.Add(T, null);
                        }
                        else
                        {

                            typeToAttrs.Add(T, pdict);
                        }
                    }
                }
            }
            return typeToAttrs[T];
        }

        /// <summary>
        /// 值 缓存键
        /// </summary>
        class ValueToCache : Dictionary<ValueBase, HashSet<string>> { }
        /// <summary>
        /// 字段
        /// </summary>
        class FieldToValue : Dictionary<string, ValueToCache> { }
        Dictionary<Type, FieldToValue> Notifies = new Dictionary<Type, FieldToValue>();
        HashSet<string> SettedCacheKeys = new HashSet<string>();
        ValueToCache GetValueToCache<T>(string field, bool autocreate)
        {
            //1.取出类型
            FieldToValue fv;
            if (!Notifies.TryGetValue(typeof(T), out fv))
            {
                if (autocreate == false) return null;
                lock (Notifies)
                {
                    if (!Notifies.TryGetValue(typeof(T), out fv))
                    {
                        fv = new FieldToValue();
                        Notifies.Add(typeof(T), fv);
                    }
                }
            }
            //2.取出字段
            ValueToCache vcset;
            if (!fv.TryGetValue(field, out vcset))
            {
                if (autocreate == false) return null;
                lock (fv)
                {
                    if (!fv.TryGetValue(field, out vcset))
                    {
                        vcset = new ValueToCache();
                        fv.Add(field, vcset);
                    }
                }
            }
            return vcset;
        }
        /// <summary>
        /// 获取缓存键列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="field"></param>
        /// <param name="autocreate"></param>
        /// <returns></returns>
        HashSet<string> GetCacheList<T>(string field, ValueBase value, bool autocreate)
        {
            var vcset = GetValueToCache<T>(field, autocreate);
            if (vcset == null) return null;
            //3.取出缓存键列表
            HashSet<string> keylist;
            if (!vcset.TryGetValue(value, out keylist))
            {
                if (autocreate == false) return null;
                keylist = new HashSet<string>();
                vcset.Add(value, keylist);
            }
            return keylist;
        }
        /// <summary>
        /// 将T类型的field字段的value值与cachekey关联，一旦更改，就删除此cachekey
        /// 不允许同一cachekey关联到了不同的值上
        /// </summary>
        public void RegisterDepend<T>(string field, ValueBase value, string cachekey)
        {
            if (SettedCacheKeys.Contains(cachekey)) return;
            var keylist = GetCacheList<T>(field, value, true);
            if (!keylist.Contains(cachekey))
            {
                keylist.Add(cachekey);
            }
        }

        /// <summary>
        /// 为缓存注册T类型依赖，当且仅当所有条件列匹配时，才进行下一步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fields"></param>
        /// <param name="cachekey"></param>
        public void RegisterDepend<T>(Dictionary<string, ValueBase> fields, string cachekey)
        {

        }

        /// <summary>
        /// 获取该表已注册的条件列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public string[] GetRegisteredFields<T>()
        {
            Type t = typeof(T);
            if (!Notifies.ContainsKey(t)) return null;
            return Notifies[t].Keys.ToArray();
        }
        static Dictionary<string, System.Reflection.PropertyInfo> fields = new Dictionary<string, System.Reflection.PropertyInfo>();
        public static object GetFieldValue<T>(T obj, string field)
        {
            string v = typeof(T).ToString() + "$" + field;
            System.Reflection.PropertyInfo info;
            if (!fields.TryGetValue(v, out info))
            {
                info = typeof(T).GetProperty(field);
                fields.Add(v, info);
            }
            return info.GetValue(obj, null);
        }

        #region 比较更改以通知缓存

        /// <summary>
        /// 由对象生成在缓存依赖中的键值对，以在下一步进行比较
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public Dictionary<string, object> GetOldValues<T>(T obj)
        {
            //1.查询已注册的条件列
            string[] fields = EntityCacheHelper.Instance.GetRegisteredFields<T>();
            if (fields == null) return null;

            //2.查询已注册的结果列
            HashSet<string> fieldset = GetRegisteredEntityFields<T>();
            fieldset.UnionWith(fields);

            Dictionary<string, object> oldValues = new Dictionary<string, object>();
            foreach (var f in fields)
            {
                oldValues.Add(f, EntityCacheHelper.GetFieldValue(obj, f));
            }
            return oldValues;
        }

        /// <summary>
        /// 与旧值比较，如果不同则更新缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="oldValues"></param>
        public void CompareValues<T>(T obj, Dictionary<string, object> oldValues)
        {
            Dictionary<string, object> changedValue = new Dictionary<string, object>();

            foreach (KeyValuePair<string, object> entry in oldValues)
            {
                object newValue = EntityCacheHelper.GetFieldValue(obj, entry.Key);
                if (newValue.Equals(entry.Value)) continue;
                changedValue.Add(entry.Key, entry.Value);
                //EntityCacheHelper.Instance.NotifyChange<T>(entry.Key, entry.Value);
            }

            if (!changedValue.Any()) return;//没有任何更改...

            //1.查找条件列匹配的
            string[] fields = EntityCacheHelper.Instance.GetRegisteredFields<T>();


            //2.查找结果列匹配的

        }

        #endregion

        /// <summary>
        /// 将数据保存到缓存并创建一系列依赖
        /// </summary>
        /// <param name="classObj">列表类对象</param>
        /// <param name="fullCacheKey">完整的缓存键</param>
        /// <param name="data"></param>
        public void RegisterEntityDepend<T>(object classObj, string fullCacheKey, object data)
        {
            //目前是任一条件列一改就失效
            var propattrs = GetTypeValueBaseProperties(classObj.GetType());
            if (propattrs != null)
            {
                foreach (KeyValuePair<System.Reflection.PropertyInfo, ValueBaseAttribute[]> item in propattrs)
                {
                    var propvalue = item.Key.GetValue(classObj, null);
                    foreach (var iv in item.Value)
                    {
                        var vb = iv.GetValue(propvalue);
                        RegisterDepend<T>(iv.Field, vb, fullCacheKey);
                    }
                }
            }
        }

        /// <summary>
        /// 通知更改
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="field"></param>
        /// <param name="value">更改的内容</param>
        public void NotifyChange<T>(string field, object value)
        {
            var vcset = GetValueToCache<T>(field, false);
            if (vcset == null) return;
            //开始查找更改
            foreach (var vc in vcset.Keys)
            {
                if (vc.IsChanged(value))
                {
                    //如果产生了更改，则从缓存中移除项
                    foreach (var key in vcset[vc])
                    {
                        HttpRuntime.Cache.Remove(key);
                    }
                }
            }
        }

        /// <summary>
        /// 当一个记录被添加或移去时调用此方法以更新列表缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        public void NotifyChange<T>(T obj)
        {
            //1.取出类型
            FieldToValue fv;
            if (!Notifies.TryGetValue(typeof(T), out fv) || !fv.Any())
            {
                return;//没有依赖
            }

            foreach (KeyValuePair<string, ValueToCache> entry in fv)
            {
                object value = GetFieldValue(obj, entry.Key);
                foreach (var vc in entry.Value.Keys)
                {
                    if (vc.IsChanged(value))
                    {
                        //如果产生了更改，则从缓存中移除项
                        foreach (var key in entry.Value[vc])
                        {
                            HttpRuntime.Cache.Remove(key);
                        }
                    }
                }
            }
        }

        public static void test()
        {
            SingleValue sv = new SingleValue { Value = "01" };
            RangeValue rv = new RangeValue { MinValue = DateTime.Today };
            //EntityCacheHelper.Instance.RegisterDepend<Talent.Entity.HR_Article>("MsgCategory", rv, "test");
            //EntityCacheHelper.Instance.NotifyChange<Talent.Entity.HR_Article>("MsgCategory", DateTime.Now);
        }
    }

    #endregion
}
